//
// Created by zhouyi on 2019-11-28.
//

#include <malloc.h>
#include <cstring>
#include "EnjoyPlayer.h"
#include "Log.h"

extern "C" {
#include <libavformat/avformat.h>
}

// 使用的友元函数
void *asyncPrepare_t(void *args) {
    EnjoyPlayer *player = static_cast<EnjoyPlayer *>(args);
    player->_prepare();
    return nullptr;
}

EnjoyPlayer::EnjoyPlayer(JavaCallHelper *helper) : helper(helper) {
    avformat_network_init();
    videoChannel = nullptr;
    //    audioChannel = nullptr;
}



void EnjoyPlayer::setDataSource(const char *path_) {
    LOGI("======================================\n");
    // C方式
//    this->path = static_cast<char *>(malloc(strlen(path_) + 1));
//    memset(path, 0, strlen(path) + 1);
//    memcpy(path, path_, strlen(path_));

    // C++方式
    path = new char[strlen(path_) + 1];
    stpcpy(path, path_);
}

void EnjoyPlayer::prepare() {
    // 解析 耗时操作
    pthread_create(&prepareTask, nullptr, asyncPrepare_t, this);
}

void EnjoyPlayer::_prepare() {
    /**
     * 获取上下文
     */
    avFormatContext = avformat_alloc_context();

    /**
     * 1. 打开媒体文件 avformat_open_input
     */

    // 参数1 指针的指针 就是为了在函数内部 能改变外部
    // 参数3 输入文件的封装格式，null表示自动检测格式avi/flv
    // 参数4 map集合，比如打网络文件 300秒失败
    AVDictionary *opts;
    av_dict_set(&opts, "timeout", "3000000", 0);
    int ret = avformat_open_input(&avFormatContext, path, nullptr, &opts);

    if (ret != 0) {
        LOGE("打开%s失败，返回：%d错误描述：%s", path, ret, av_err2str(ret));
        helper->onError(FFMPEG_CAN_NOT_OPEN_URL, THREAD_CHILD);
        return;
    }


    /**
     * 2. 查找媒体信息 avformat_find_stream_info
     */
    ret = avformat_find_stream_info(avFormatContext, &opts);
    if (ret < 0) {
        LOGE("查找媒体流 %s 失败，返回:%d 错误描述:%s", path, ret, av_err2str(ret));
        helper->onError(FFMPEG_CAN_NOT_FIND_STREAMS, THREAD_CHILD);
        return;
    }
    // 视频的时间长度单位是秒
    duration = avFormatContext->duration / AV_TIME_BASE;

    // 媒体文件中媒体流数量（视频流，音频流）
    for (int channelId = 0; channelId < avFormatContext->nb_streams; ++channelId) {
        AVStream *avStream = avFormatContext->streams[channelId];
        // 流中包含解码信息
        AVCodecParameters *parameters = avStream->codecpar;

        /**
         * 3. 查找解码器   avcodec_find_decoder
         * 可能是返回的NULL
         */
        AVCodec *dec = avcodec_find_decoder(parameters->codec_id);

        if (!dec) {
            helper->onError(FFMPEG_FIND_DECODER_FAIL, THREAD_CHILD);
            return;
        }

        /**
         * 4. 打开解码器(申请内存)
         */
        AVCodecContext *avCodecContext = avcodec_alloc_context3(dec);
        if (!avCodecContext) {
            helper->onError(FFMPEG_ALLOC_CODEC_CONTEXT_FAIL, THREAD_CHILD);
            return;
        }
        // 把解码的信息数据赋值给解码上下文中的成员
        ret = avcodec_parameters_to_context(avCodecContext, parameters);

        if (ret < 0) {
            helper->onError(FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL, THREAD_CHILD);
            return;
        }
        // 打开解码器
        ret = avcodec_open2(avCodecContext, dec, nullptr);

        if (ret != 0) {
            // 打开失败
            helper->onError(FFMPEG_OPEN_DECODER_FAIL, THREAD_CHILD);
            return;
        }

        if (parameters->codec_type == AVMEDIA_TYPE_VIDEO) {
            // 帧率
            int fps = static_cast<int>(av_q2d(avStream->avg_frame_rate));
            videoChannel = new VideoChannel(channelId, helper, avCodecContext, avStream->time_base, fps);
            videoChannel->setWindow(window);

        } else if (parameters->codec_type == AVMEDIA_TYPE_AUDIO) {
//            audioChannel = new AudioChannel(channelId, helper, avCodecContext, avStream->time_base);
        }

    }


    if (!videoChannel /*&& !audioChannel*/) {
        // 没有视频 没有音频
        helper->onError(FFMPEG_NOMEDIA, THREAD_CHILD);
        return;
    }


    /**
     * 告诉java准备好了
     */

    helper->onPrepared(THREAD_CHILD);


}

/**
 * 子线程执行的方法
 * @param args
 * @return
 */
void *asyncStart_t(void *args) {

    EnjoyPlayer *player = static_cast<EnjoyPlayer *>(args);

    player->_start();
}

void EnjoyPlayer::start() {
    /**
     * 1. 读取媒体源的数据
     * 2. 根据数据类型放入Audio/VideoChannel的队列
     */

    isPlaying = true;
    if (videoChannel) {
        videoChannel->play();
    }

    pthread_create(&startTask, nullptr, asyncStart_t, this);

}

void EnjoyPlayer::_start() {
    int ret;
    while (isPlaying) {
        AVPacket *avPacket = av_packet_alloc();
        ret = av_read_frame(avFormatContext, avPacket);
        if (ret == 0) {
            if (videoChannel && avPacket->stream_index == videoChannel->channelId) {
                videoChannel->pkt_queue.enQueue(avPacket);
            }
        } else if (ret == AVERROR_EOF) { // end of file
            // 读取完成不代表是播放完成了
            if (videoChannel->pkt_queue.empty() && videoChannel->frame_queue.empty()) {
                // 播放完毕
                break;
            }

        } else {
            break;
        }
    }

    isPlaying = 0;
    videoChannel->stop();
}

void EnjoyPlayer::setWindow(ANativeWindow *window) {
    this->window = window;
    if (videoChannel) {
        videoChannel->setWindow(window);
    }
}




